﻿using System;
using System.Net.Security;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Security.Cryptography.X509Certificates;

namespace Heartbeat
{

    public class SslConnection : TcpConnection
    {
        protected SslStream mSslStream;
        protected const int SSL_BUFFER_LENGTH = 16384;
        protected byte[] mBuffer = new byte[SSL_BUFFER_LENGTH];
        protected StringBuilder mReadData = new StringBuilder();

        public SslConnection() : base(true)
        {

        }

        protected override void OnConnected()
        {
            X509CertificateCollection cCollection = new X509CertificateCollection();
            Console.WriteLine(System.IO.Directory.GetCurrentDirectory());
            cCollection.Add(new X509Certificate("..\\..\\cert.der"));
            mSslStream = new SslStream(new System.Net.Sockets.NetworkStream(mSocket), false, new RemoteCertificateValidationCallback(ValidateServerCertificate));
            mSslStream.BeginAuthenticateAsClient(
                        mAddress,
                        cCollection,
                        System.Security.Authentication.SslProtocols.Default,
                        false,
                        new AsyncCallback(AuthenticateCallback),
                        mSslStream);
        }

        public override void Write(Packet pPacket)
        {
            JObject msgJson = new JObject();
            JObject headerJson = new JObject();
            headerJson["channel"] = pPacket.GetChannel();
            msgJson["header"] = headerJson;
            msgJson["message"] = pPacket.GetMessage();

            
            string msgStr = msgJson.ToString(Formatting.None);

            byte[] message = Encoding.UTF8.GetBytes(msgStr);
            // Asynchronously send the message to the client.
            mSslStream.BeginWrite(message, 0, message.Length,
                new AsyncCallback(SendCallback),
                new StateObject(message.Length));
        }

        protected new void AuthenticateCallback(IAsyncResult ar)
        {
            OnReady();
        }

        protected void SendCallback(IAsyncResult ar)
        {
            mSslStream.EndWrite(ar);
        }

        protected override void StartReading()
        {
            try
            {
                // Begin receiving the data from the remote device.
                mSslStream.BeginRead(mBuffer, 0, SSL_BUFFER_LENGTH,
                    new AsyncCallback(ReadSslCallback), mSslStream);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public static new bool ValidateServerCertificate(object sender, X509Certificate certificate,
        X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            Console.WriteLine("VALIDATE SERVER CERTIFICATE");
            // Accept all certificates
            return true;
        }

        protected void ReadSslCallback(IAsyncResult ar)
        {
            // Read the  message sent by the server. 
            // The end of the message is signaled using the 
            // "<EOF>" marker.
            int byteCount = -1;
                Console.WriteLine("Reading data from the server.");
                byteCount = mSslStream.EndRead(ar);
                Console.WriteLine(byteCount);
                // Use Decoder class to convert from bytes to UTF8 
                // in case a character spans two buffers.
                Decoder decoder = Encoding.UTF8.GetDecoder();
                char[] chars = new char[decoder.GetCharCount(mBuffer, 0, byteCount)];
                decoder.GetChars(mBuffer, 0, byteCount, chars, 0);
                mReadData.Append(chars);
                Console.WriteLine(mReadData.ToString());
                MessageReceived(mReadData.ToString());
                mReadData.Clear();
                mSslStream.BeginRead(mBuffer, 0, SSL_BUFFER_LENGTH,
                        new AsyncCallback(ReadSslCallback),
                        mSslStream);

                // Check for EOF or an empty message.
                /*
                int eofIndex = mReadData.ToString().IndexOf("<EOF>");
                
                Console.WriteLine("Eof index = " + eofIndex);
                if (mReadData.ToString().IndexOf("<EOF>") == -1 && byteCount != 0)
                {
                    // We are not finished reading. 
                    // Asynchronously read more message data from  the server.
                    mSslStream.BeginRead(mBuffer, 0, SSL_BUFFER_LENGTH,
                        new AsyncCallback(ReadSslCallback),
                        mSslStream);
                }
                else
                {
                    Console.WriteLine("Message from the server: {0}", mReadData.ToString());
                    MessageReceived(mReadData.ToString());
                }*/
        }
    }
}
